home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / c-client / nntpcvms.c < prev    next >
C/C++ Source or Header  |  1996-05-02  |  35KB  |  1,192 lines

  1. /*
  2.  * Program:    Network News Transfer Protocol (NNTP) client routines - VMS
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    2 August 1994
  13.  * Last Edited:    2 May 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notices appear in all copies and that both the
  20.  * above copyright notices and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  30.  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN
  32.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include "mail.h"
  38. #include "osdep.h"
  39. #include <unixio.h>
  40. #include <ctype.h>
  41. #include <stdio.h>
  42. #include <errno.h>
  43. extern int errno;        /* just in case */
  44. #include <sys/file.h>
  45. #include <sys/stat.h>
  46. #include "smtp.h"
  47. #include "nntp.h"
  48. #include "nntpcvms.h"
  49. #include "rfc822.h"
  50. #include "misc.h"
  51. #include "newsrc.h"
  52.  
  53. /* NNTP mail routines */
  54.  
  55.  
  56. /* Driver dispatch used by MAIL */
  57.  
  58. DRIVER nntpdriver = {
  59.   "nntp",            /* driver name */
  60.   (DRIVER *) NIL,        /* next driver */
  61.   nntp_valid,            /* mailbox is valid for us */
  62.   nntp_parameters,        /* manipulate parameters */
  63.   nntp_find,            /* find mailboxes */
  64.   nntp_find_bboards,        /* find bboards */
  65.   nntp_find_all,        /* find all mailboxes */
  66.   nntp_find_all_bboards,    /* find all bboards */
  67.   nntp_subscribe,        /* subscribe to mailbox */
  68.   nntp_unsubscribe,        /* unsubscribe from mailbox */
  69.   nntp_subscribe_bboard,    /* subscribe to bboard */
  70.   nntp_unsubscribe_bboard,    /* unsubscribe from bboard */
  71.   nntp_create,            /* create mailbox */
  72.   nntp_delete,            /* delete mailbox */
  73.   nntp_rename,            /* rename mailbox */
  74.   nntp_mopen,            /* open mailbox */
  75.   nntp_close,            /* close mailbox */
  76.   nntp_fetchfast,        /* fetch message "fast" attributes */
  77.   nntp_fetchflags,        /* fetch message flags */
  78.   nntp_fetchstructure,        /* fetch message envelopes */
  79.   nntp_fetchheader,        /* fetch message header only */
  80.   nntp_fetchtext,        /* fetch message body only */
  81.   nntp_fetchbody,        /* fetch message body section */
  82.   nntp_setflag,            /* set message flag */
  83.   nntp_clearflag,        /* clear message flag */
  84.   nntp_search,            /* search for message based on criteria */
  85.   nntp_ping,            /* ping mailbox to see if still alive */
  86.   nntp_check,            /* check for new messages */
  87.   nntp_expunge,            /* expunge deleted messages */
  88.   nntp_copy,            /* copy messages to another mailbox */
  89.   nntp_move,            /* move messages to another mailbox */
  90.   nntp_append,            /* append string message to mailbox */
  91.   nntp_gc            /* garbage collect stream */
  92. };
  93.  
  94.                 /* prototype stream */
  95. MAILSTREAM nntpproto = {&nntpdriver};
  96.  
  97. /* NNTP mail validate mailbox
  98.  * Accepts: mailbox name
  99.  * Returns: our driver if name is valid, NIL otherwise
  100.  */
  101.  
  102. DRIVER *nntp_valid (char *name)
  103. {
  104.                 /* must be bboard */
  105.   return *name == '*' ? mail_valid_net (name,&nntpdriver,NIL,NIL) : NIL;
  106. }
  107.  
  108.  
  109. /* News manipulate driver parameters
  110.  * Accepts: function code
  111.  *        function-dependent value
  112.  * Returns: function-dependent return value
  113.  */
  114.  
  115. void *nntp_parameters (long function,void *value)
  116. {
  117.   return NIL;
  118. }
  119.  
  120. /* NNTP mail find list of mailboxes
  121.  * Accepts: mail stream
  122.  *        pattern to search
  123.  */
  124.  
  125. void nntp_find (MAILSTREAM *stream,char *pat)
  126. {
  127.   /* Always a no-op */
  128. }
  129.  
  130.  
  131. /* NNTP mail find list of bboards
  132.  * Accepts: mail stream
  133.  *        pattern to search
  134.  */
  135.  
  136. void nntp_find_bboards (MAILSTREAM *stream,char *pat)
  137. {
  138.   char *t;
  139.   void *s = NIL;
  140.                 /* use .newsrc if a stream given */
  141.   if (stream && !stream->anonymous) newsrc_find (pat);
  142.   else while (t = sm_read (&s))    /* use subscription manager if no stream */
  143.     if ((*t == '*') && pmatch (t+1,pat)) mm_bboard (t+1);
  144. }
  145.  
  146. /* NNTP mail find list of all mailboxes
  147.  * Accepts: mail stream
  148.  *        pattern to search
  149.  */
  150.  
  151. void nntp_find_all (MAILSTREAM *stream,char *pat)
  152. {
  153.   /* Always a no-op */
  154. }
  155.  
  156.  
  157. /* NNTP mail find list of all bboards
  158.  * Accepts: mail stream
  159.  *        pattern to search
  160.  */
  161.  
  162. void nntp_find_all_bboards (MAILSTREAM *stream,char *pat)
  163. {
  164.   char *s,*t,*bbd,*patx,tmp[MAILTMPLEN];
  165.                 /* use .newsrc if a stream given */
  166.   if (stream && LOCAL && LOCAL->nntpstream) {
  167.                 /* begin with a host specification? */
  168.     if (((*pat == '{') || ((*pat == '*') && (pat[1] == '{'))) &&
  169.     (t = strchr (pat,'}')) && *(patx = ++t)) {
  170.       if (*pat == '*') pat++;    /* yes, skip leading * (old Pine behavior) */
  171.       strcpy (tmp,pat);        /* copy host name */
  172.       bbd = tmp + (patx - pat);    /* where we write the bboards */
  173.     }
  174.     else {            /* no host specification */
  175.       bbd = tmp;        /* no prefix */
  176.       patx = pat;        /* use entire specification */
  177.     }
  178.                 /* ask server for all active newsgroups */
  179.     if (!(nntp_send (LOCAL->nntpstream,"LIST","ACTIVE") == NNTPGLIST)) return;
  180.                 /* process data until we see final dot */
  181.     while ((s = tcp_getline (LOCAL->nntpstream->tcpstream)) && *s != '.') {
  182.                 /* tie off after newsgroup name */
  183.       if (t = strchr (s,' ')) *t = '\0';
  184.       if (pmatch (s,patx)) {    /* report to main program if have match */
  185.     strcpy (bbd,s);        /* write newsgroup name after prefix */
  186.     mm_bboard (tmp);
  187.       }
  188.       fs_give ((void **) &s);    /* clean up */
  189.     }
  190.   }
  191. }
  192.  
  193. /* NNTP mail subscribe to mailbox
  194.  * Accepts: mail stream
  195.  *        mailbox to add to subscription list
  196.  * Returns: T on success, NIL on failure
  197.  */
  198.  
  199. long nntp_subscribe (MAILSTREAM *stream,char *mailbox)
  200. {
  201.   return NIL;            /* never valid for NNTP */
  202. }
  203.  
  204.  
  205. /* NNTP mail unsubscribe to mailbox
  206.  * Accepts: mail stream
  207.  *        mailbox to delete from subscription list
  208.  * Returns: T on success, NIL on failure
  209.  */
  210.  
  211. long nntp_unsubscribe (MAILSTREAM *stream,char *mailbox)
  212. {
  213.   return NIL;            /* never valid for NNTP */
  214. }
  215.  
  216.  
  217. /* NNTP mail subscribe to bboard
  218.  * Accepts: mail stream
  219.  *        bboard to add to subscription list
  220.  * Returns: T on success, NIL on failure
  221.  */
  222.  
  223. long nntp_subscribe_bboard (MAILSTREAM *stream,char *mailbox)
  224. {
  225.   char *s = strchr (mailbox,'}');
  226.   return s ? newsrc_update (s+1,':') : NIL;
  227. }
  228.  
  229.  
  230. /* NNTP mail unsubscribe to bboard
  231.  * Accepts: mail stream
  232.  *        bboard to delete from subscription list
  233.  * Returns: T on success, NIL on failure
  234.  */
  235.  
  236. long nntp_unsubscribe_bboard (MAILSTREAM *stream,char *mailbox)
  237. {
  238.   char *s = strchr (mailbox,'}');
  239.   return s ? newsrc_update (s+1,'!') : NIL;
  240. }
  241.  
  242. /* NNTP mail create mailbox
  243.  * Accepts: mail stream
  244.  *        mailbox name to create
  245.  * Returns: T on success, NIL on failure
  246.  */
  247.  
  248. long nntp_create (MAILSTREAM *stream,char *mailbox)
  249. {
  250.   return NIL;            /* never valid for NNTP */
  251. }
  252.  
  253.  
  254. /* NNTP mail delete mailbox
  255.  *        mailbox name to delete
  256.  * Returns: T on success, NIL on failure
  257.  */
  258.  
  259. long nntp_delete (MAILSTREAM *stream,char *mailbox)
  260. {
  261.   return NIL;            /* never valid for NNTP */
  262. }
  263.  
  264.  
  265. /* NNTP mail rename mailbox
  266.  * Accepts: mail stream
  267.  *        old mailbox name
  268.  *        new mailbox name
  269.  * Returns: T on success, NIL on failure
  270.  */
  271.  
  272. long nntp_rename (MAILSTREAM *stream,char *old,char *new)
  273. {
  274.   return NIL;            /* never valid for NNTP */
  275. }
  276.  
  277. /* NNTP mail open
  278.  * Accepts: stream to open
  279.  * Returns: stream on success, NIL on failure
  280.  */
  281.  
  282. MAILSTREAM *nntp_mopen (MAILSTREAM *stream)
  283. {
  284.   long i,j,k;
  285.   long nmsgs = 0;
  286.   long unseen = 0;
  287.   char c = NIL,*s,*t,tmp[MAILTMPLEN];
  288.   NETMBX mb;
  289.   void *tcpstream;
  290.   SMTPSTREAM *nstream = NIL;
  291.                 /* return prototype for OP_PROTOTYPE call */
  292.   if (!stream) return &nntpproto;
  293.   mail_valid_net_parse (stream->mailbox,&mb);
  294.   if (!*mb.mailbox) strcpy (mb.mailbox,"general");
  295.   if (LOCAL) {            /* if recycle stream, see if changing hosts */
  296.     if (strcmp (lcase (mb.host),lcase (strcpy (tmp,LOCAL->host)))) {
  297.       sprintf (tmp,"Closing connection to %s",LOCAL->host);
  298.       if (!stream->silent) mm_log (tmp,(long) NIL);
  299.     }
  300.     else {            /* same host, preserve NNTP connection */
  301.       sprintf (tmp,"Reusing connection to %s",LOCAL->host);
  302.       if (!stream->silent) mm_log (tmp,(long) NIL);
  303.       nstream = LOCAL->nntpstream;
  304.       LOCAL->nntpstream = NIL;    /* keep nntp_close() from punting it */
  305.     }
  306.     nntp_close (stream);    /* do close action */
  307.     stream->dtb = &nntpdriver;/* reattach this driver */
  308.     mail_free_cache (stream);    /* clean up cache */
  309.   }
  310.                 /* in case /debug switch given */
  311.   if (mb.dbgflag) stream->debug = T;
  312.  
  313.   if (!nstream) {        /* open NNTP now if not already open */
  314.     char *hostlist[2];
  315.     hostlist[0] = strcpy (tmp,mb.host);
  316.     if (mb.port) sprintf (tmp + strlen (tmp),":%ld",mb.port);
  317.     hostlist[1] = NIL;
  318.     nstream = nntp_open (hostlist,OP_READONLY+(stream->debug ? OP_DEBUG:NIL));
  319.   }
  320.   if (nstream) {        /* now try to open newsgroup */
  321.     if ((!stream->halfopen) &&    /* open the newsgroup if not halfopen */
  322.     ((nntp_send (nstream,"GROUP",mb.mailbox) != NNTPGOK) ||
  323.      ((k = strtol (nstream->reply + 4,&s,10)) < 0) ||
  324.      ((i = strtol (s,&s,10)) < 0) || ((j = strtol (s,&s,10)) < 0) ||
  325.      (nmsgs = i | j ? 1 + j - i : 0) < 0)) {
  326.       mm_log (nstream->reply,ERROR);
  327.       smtp_close (nstream);    /* punt stream */
  328.       nstream = NIL;
  329.       return NIL;
  330.     }
  331.                 /* newsgroup open, instantiate local data */
  332.     stream->local = fs_get (sizeof (NNTPLOCAL));
  333.     LOCAL->nntpstream = nstream;
  334.     LOCAL->dirty = NIL;        /* no update to .newsrc needed yet */
  335.                 /* copy host and newsgroup name */
  336.     LOCAL->host = cpystr (mb.host);
  337.     LOCAL->name = cpystr (mb.mailbox);
  338.     stream->sequence++;        /* bump sequence number */
  339.     stream->rdonly = T;        /* make sure higher level knows readonly */
  340.     LOCAL->number = NIL;
  341.     LOCAL->header = LOCAL->body = NIL;
  342.     LOCAL->buf = NIL;
  343.  
  344.     if (!stream->halfopen) {    /* if not half-open */
  345.       if (nmsgs) {        /* find what messages exist */
  346.     LOCAL->number = (unsigned long *) fs_get (nmsgs*sizeof(unsigned long));
  347.     sprintf (tmp,"%ld-%ld",i,j);
  348.     if ((nntp_send (nstream,"LISTGROUP",mb.mailbox) == NNTPGOK) ||
  349.         (nntp_send (nstream,"XHDR Date",tmp) == NNTPHEAD)) {
  350.       for (i = 0; (s = tcp_getline (nstream->tcpstream)) && strcmp (s,".");
  351.            i++) {        /* initialize c-client/NNTP map */
  352.         if (i < nmsgs) LOCAL->number[i] = atol (s);
  353.         fs_give ((void **) &s);
  354.       }
  355.       if (s) fs_give ((void **) &s);
  356.       if (i != nmsgs) {    /* found holes? */
  357.         if (nmsgs = i)
  358.           fs_resize ((void **) &LOCAL->number,nmsgs*sizeof(unsigned long));
  359.         else fs_give ((void **) &LOCAL->number);
  360.       }
  361.     }
  362.                 /* assume c-client/NNTP map is entire range */
  363.     else for (k = 0; k < nmsgs; ++k) LOCAL->number[k] = i + k;
  364.                 /* create caches */
  365.     LOCAL->header = (char **) fs_get (nmsgs * sizeof (char *));
  366.     LOCAL->body = (char **) fs_get (nmsgs * sizeof (char *));
  367.                 /* initialize per-message cache */
  368.     for (i = 0; i < nmsgs; ++i) LOCAL->header[i] = LOCAL->body[i] = NIL;
  369.       }
  370.                 /* make temporary buffer */
  371.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = MAXMESSAGESIZE) + 1);
  372.                 /* notify upper level that messages exist */
  373.       mail_exists (stream,nmsgs);
  374.                 /* read .newsrc entries */
  375.       mail_recent (stream,newsrc_read (LOCAL->name,stream,LOCAL->number));
  376.                 /* notify if empty bboard */
  377.       if (!(stream->nmsgs || stream->silent)) {
  378.     sprintf (tmp,"Newsgroup %s is empty",LOCAL->name);
  379.     mm_log (tmp,WARN);
  380.       }
  381.     }
  382.   }
  383.   return LOCAL ? stream : NIL;    /* if stream is alive, return to caller */
  384. }
  385.  
  386. /* NNTP mail close
  387.  * Accepts: MAIL stream
  388.  */
  389.  
  390. void nntp_close (MAILSTREAM *stream)
  391. {
  392.   if (LOCAL) {            /* only if a file is open */
  393.     nntp_check (stream);    /* dump final checkpoint */
  394.     if (LOCAL->name) fs_give ((void **) &LOCAL->name);
  395.     if (LOCAL->host) fs_give ((void **) &LOCAL->host);
  396.     nntp_gc (stream,GC_TEXTS);    /* free local cache */
  397.     if (LOCAL->number) fs_give ((void **) &LOCAL->number);
  398.     if (LOCAL->header) fs_give ((void **) &LOCAL->header);
  399.     if (LOCAL->body) fs_give ((void **) &LOCAL->body);
  400.                 /* free local scratch buffer */
  401.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  402.                 /* close NNTP connection */
  403.     if (LOCAL->nntpstream) smtp_close (LOCAL->nntpstream);
  404.                 /* nuke the local data */
  405.     fs_give ((void **) &stream->local);
  406.     stream->dtb = NIL;        /* log out the DTB */
  407.   }
  408. }
  409.  
  410. /* NNTP mail fetch fast information
  411.  * Accepts: MAIL stream
  412.  *        sequence
  413.  */
  414.  
  415. void nntp_fetchfast (MAILSTREAM *stream,char *sequence)
  416. {
  417.   long i;
  418.   BODY *b;
  419.                 /* ugly and slow */
  420.   if (stream && LOCAL && mail_sequence (stream,sequence))
  421.     for (i = 1; i <= stream->nmsgs; i++)
  422.       if (mail_elt (stream,i)->sequence)
  423.     nntp_fetchstructure (stream,i,&b);
  424. }
  425.  
  426.  
  427. /* NNTP mail fetch flags
  428.  * Accepts: MAIL stream
  429.  *        sequence
  430.  */
  431.  
  432. void nntp_fetchflags (MAILSTREAM *stream,char *sequence)
  433. {
  434.   return;            /* no-op for local mail */
  435. }
  436.  
  437. /* NNTP mail fetch envelope
  438.  * Accepts: MAIL stream
  439.  *        message # to fetch
  440.  *        pointer to return body
  441.  * Returns: envelope of this message, body returned in body value
  442.  *
  443.  * Fetches the "fast" information as well
  444.  */
  445.  
  446. ENVELOPE *nntp_fetchstructure (MAILSTREAM *stream,long msgno,BODY **body)
  447. {
  448.   char *h,*t;
  449.   LONGCACHE *lelt;
  450.   ENVELOPE **env;
  451.   STRING bs;
  452.   BODY **b;
  453.   unsigned long hdrsize;
  454.   unsigned long textsize = 0;
  455.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  456.   if (stream->scache) {        /* short cache */
  457.     if (msgno != stream->msgno){/* flush old poop if a different message */
  458.       mail_free_envelope (&stream->env);
  459.       mail_free_body (&stream->body);
  460.     }
  461.     stream->msgno = msgno;
  462.     env = &stream->env;        /* get pointers to envelope and body */
  463.     b = &stream->body;
  464.   }
  465.   else {            /* long cache */
  466.     lelt = mail_lelt (stream,msgno);
  467.     env = &lelt->env;        /* get pointers to envelope and body */
  468.     b = &lelt->body;
  469.   }
  470.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  471.     mail_free_envelope (env);    /* flush old envelope and body */
  472.     mail_free_body (b);
  473.     hdrsize = strlen (h = nntp_fetchheader (stream,msgno));
  474.     if (body) {            /* only if want to parse body */
  475.       textsize = strlen (t = nntp_fetchtext_work (stream,msgno));
  476.                 /* calculate message size */
  477.       elt->rfc822_size = hdrsize + textsize;
  478.       INIT (&bs,mail_string,(void *) t,textsize);
  479.     }
  480.                 /* parse envelope and body */
  481.     rfc822_parse_msg (env,body ? b : NIL,h,hdrsize,body ? &bs : NIL,BADHOST,
  482.               LOCAL->buf);
  483.                 /* parse date */
  484.     if (*env && (*env)->date) mail_parse_date (elt,(*env)->date);
  485.     if (!elt->month) mail_parse_date (elt,"01-JAN-1969 00:00:00 GMT");
  486.   }
  487.   if (body) *body = *b;        /* return the body */
  488.   return *env;            /* return the envelope */
  489. }
  490.  
  491. /* NNTP mail fetch message header
  492.  * Accepts: MAIL stream
  493.  *        message # to fetch
  494.  * Returns: message header in RFC822 format
  495.  */
  496.  
  497. char *nntp_fetchheader (MAILSTREAM *stream,long msgno)
  498. {
  499.   char tmp[MAILTMPLEN];
  500.   long m = msgno - 1;
  501.   if (!LOCAL->header[m]) {    /* fetch header if don't have already */
  502.     sprintf (tmp,"%ld",LOCAL->number[m]);
  503.     if (nntp_send (LOCAL->nntpstream,"HEAD",tmp) == NNTPHEAD)
  504.       LOCAL->header[m] = nntp_slurp (stream);
  505.                 /* failed, mark as deleted */
  506.     else mail_elt (stream,msgno)->deleted = T;
  507.   }
  508.   return LOCAL->header[m] ? LOCAL->header[m] : "";
  509. }
  510.  
  511.  
  512. /* NNTP mail fetch message text (body only)
  513.  * Accepts: MAIL stream
  514.  *        message # to fetch
  515.  * Returns: message text in RFC822 format
  516.  */
  517.  
  518. char *nntp_fetchtext (MAILSTREAM *stream,long msgno)
  519. {
  520.   long m = msgno - 1;
  521.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  522.   elt->seen = T;        /* mark as seen */
  523.   return nntp_fetchtext_work (stream,msgno);
  524. }
  525.  
  526.  
  527. /* NNTP mail fetch message text work
  528.  * Accepts: MAIL stream
  529.  *        message # to fetch
  530.  * Returns: message text in RFC822 format
  531.  */
  532.  
  533. char *nntp_fetchtext_work (MAILSTREAM *stream,long msgno)
  534. {
  535.   char tmp[MAILTMPLEN];
  536.   long m = msgno - 1;
  537.   if (!LOCAL->body[m]) {    /* fetch body if don't have already */
  538.     sprintf (tmp,"%ld",LOCAL->number[m]);
  539.     if (nntp_send (LOCAL->nntpstream,"BODY",tmp) == NNTPBODY)
  540.       LOCAL->body[m] = nntp_slurp (stream);
  541.                 /* failed, mark as deleted */
  542.     else mail_elt (stream,msgno)->deleted = T;
  543.   }
  544.   return LOCAL->body[m] ? LOCAL->body[m] : "";
  545. }
  546.  
  547. /* NNTP mail slurp NNTP dot-terminated text
  548.  * Accepts: MAIL stream
  549.  * Returns: text
  550.  */
  551.  
  552. char *nntp_slurp (MAILSTREAM *stream)
  553. {
  554.   char *s,*t;
  555.   unsigned long i;
  556.   unsigned long bufpos = 0;
  557.   while (s = tcp_getline (LOCAL->nntpstream->tcpstream)) {
  558.     if (*s == '.') {        /* possible end of text? */
  559.       if (s[1]) t = s + 1;    /* pointer to true start of line */
  560.       else {
  561.     fs_give ((void **) &s);    /* free the line */
  562.     break;            /* end of data */
  563.       }
  564.     }
  565.     else t = s;            /* want the entire line */
  566.                 /* ensure have enough room */
  567.     if (LOCAL->buflen < (bufpos + (i = strlen (t)) + 5))
  568.       fs_resize ((void **) &LOCAL->buf,LOCAL->buflen += (MAXMESSAGESIZE + 1));
  569.                 /* copy the text */
  570.     strncpy (LOCAL->buf + bufpos,t,i);
  571.     bufpos += i;        /* set new buffer position */
  572.     LOCAL->buf[bufpos++] = '\015';
  573.     LOCAL->buf[bufpos++] = '\012';
  574.     fs_give ((void **) &s);    /* free the line */
  575.   }
  576.   LOCAL->buf[bufpos++] = '\015';/* add final newline */
  577.   LOCAL->buf[bufpos++] = '\012';
  578.   LOCAL->buf[bufpos++] = '\0';    /* tie off string with NUL */
  579.   return cpystr (LOCAL->buf);    /* return copy of collected string */
  580. }
  581.  
  582. /* NNTP fetch message body as a structure
  583.  * Accepts: Mail stream
  584.  *        message # to fetch
  585.  *        section specifier
  586.  *        pointer to length
  587.  * Returns: pointer to section of message body
  588.  */
  589.  
  590. char *nntp_fetchbody (MAILSTREAM *stream,long m,char *s,unsigned long *len)
  591. {
  592.   BODY *b;
  593.   PART *pt;
  594.   unsigned long i;
  595.   char *base;
  596.   unsigned long offset = 0;
  597.   MESSAGECACHE *elt = mail_elt (stream,m);
  598.                 /* make sure have a body */
  599.   if (!(nntp_fetchstructure (stream,m,&b) && b && s && *s &&
  600.     ((i = strtol (s,&s,10)) > 0) &&
  601.     (base = nntp_fetchtext_work (stream,m))))
  602.     return NIL;
  603.   do {                /* until find desired body part */
  604.                 /* multipart content? */
  605.     if (b->type == TYPEMULTIPART) {
  606.       pt = b->contents.part;    /* yes, find desired part */
  607.       while (--i && (pt = pt->next));
  608.       if (!pt) return NIL;    /* bad specifier */
  609.                 /* note new body, check valid nesting */
  610.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  611.       offset = pt->offset;    /* get new offset */
  612.     }
  613.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  614.                 /* need to go down further? */
  615.     if (i = *s) switch (b->type) {
  616.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  617.       offset = b->contents.msg.offset;
  618.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  619.     case TYPEMULTIPART:        /* multipart, get next section */
  620.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  621.     default:            /* bogus subpart specification */
  622.       return NIL;
  623.     }
  624.   } while (i);
  625.                 /* lose if body bogus */
  626.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  627.   elt->seen = T;        /* mark as seen */
  628.   return rfc822_contents (&LOCAL->buf,&LOCAL->buflen,len,base + offset,
  629.               b->size.ibytes,b->encoding);
  630. }
  631.  
  632. /* NNTP mail set flag
  633.  * Accepts: MAIL stream
  634.  *        sequence
  635.  *        flag(s)
  636.  */
  637.  
  638. void nntp_setflag (MAILSTREAM *stream,char *sequence,char *flag)
  639. {
  640.   MESSAGECACHE *elt;
  641.   long i;
  642.   short f = nntp_getflags (stream,flag);
  643.   if (!f) return;        /* no-op if no flags to modify */
  644.                 /* get sequence and loop on it */
  645.   if (mail_sequence (stream,sequence)) for (i = 0; i < stream->nmsgs; i++)
  646.     if ((elt = mail_elt (stream,i + 1))->sequence) {
  647.       if (f&fSEEN) elt->seen=T;    /* set all requested flags */
  648.       if (f&fDELETED) {        /* deletion also purges the cache */
  649.     elt->deleted = T;    /* mark deleted */
  650.     LOCAL->dirty = T;    /* mark dirty */
  651.     if (LOCAL->header[i]) fs_give ((void **) &LOCAL->header[i]);
  652.     if (LOCAL->body[i]) fs_give ((void **) &LOCAL->body[i]);
  653.       }
  654.       if (f&fFLAGGED) elt->flagged = T;
  655.       if (f&fANSWERED) elt->answered = T;
  656.     }
  657. }
  658.  
  659.  
  660. /* NNTP mail clear flag
  661.  * Accepts: MAIL stream
  662.  *        sequence
  663.  *        flag(s)
  664.  */
  665.  
  666. void nntp_clearflag (MAILSTREAM *stream,char *sequence,char *flag)
  667. {
  668.   MESSAGECACHE *elt;
  669.   long i;
  670.   short f = nntp_getflags (stream,flag);
  671.   if (!f) return;        /* no-op if no flags to modify */
  672.                 /* get sequence and loop on it */
  673.   if (mail_sequence (stream,sequence)) for (i = 0; i < stream->nmsgs; i++)
  674.     if ((elt = mail_elt (stream,i + 1))->sequence) {
  675.                 /* clear all requested flags */
  676.       if (f&fSEEN) elt->seen = NIL;
  677.       if (f&fDELETED) {
  678.     elt->deleted = NIL;    /* undelete */
  679.     LOCAL->dirty = T;    /* mark stream as dirty */
  680.       }
  681.       if (f&fFLAGGED) elt->flagged = NIL;
  682.       if (f&fANSWERED) elt->answered = NIL;
  683.     }
  684. }
  685.  
  686. /* NNTP mail search for messages
  687.  * Accepts: MAIL stream
  688.  *        search criteria
  689.  */
  690.  
  691. void nntp_search (MAILSTREAM *stream,char *criteria)
  692. {
  693.   long i,n;
  694.   char *d;
  695.   search_t f;
  696.                 /* initially all searched */
  697.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  698.                 /* get first criterion */
  699.   if (criteria && (criteria = strtok (criteria," "))) {
  700.                 /* for each criterion */
  701.     for (; criteria; (criteria = strtok (NIL," "))) {
  702.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  703.       switch (*ucase (criteria)) {
  704.       case 'A':            /* possible ALL, ANSWERED */
  705.     if (!strcmp (criteria+1,"LL")) f = nntp_search_all;
  706.     else if (!strcmp (criteria+1,"NSWERED")) f = nntp_search_answered;
  707.     break;
  708.       case 'B':            /* possible BCC, BEFORE, BODY */
  709.     if (!strcmp (criteria+1,"CC"))
  710.       f = nntp_search_string (nntp_search_bcc,&d,&n);
  711.     else if (!strcmp (criteria+1,"EFORE"))
  712.       f = nntp_search_date (nntp_search_before,&n);
  713.     else if (!strcmp (criteria+1,"ODY"))
  714.       f = nntp_search_string (nntp_search_body,&d,&n);
  715.     break;
  716.       case 'C':            /* possible CC */
  717.     if (!strcmp (criteria+1,"C")) 
  718.       f = nntp_search_string (nntp_search_cc,&d,&n);
  719.     break;
  720.       case 'D':            /* possible DELETED */
  721.     if (!strcmp (criteria+1,"ELETED")) f = nntp_search_deleted;
  722.     break;
  723.       case 'F':            /* possible FLAGGED, FROM */
  724.     if (!strcmp (criteria+1,"LAGGED")) f = nntp_search_flagged;
  725.     else if (!strcmp (criteria+1,"ROM"))
  726.       f = nntp_search_string (nntp_search_from,&d,&n);
  727.     break;
  728.       case 'K':            /* possible KEYWORD */
  729.     if (!strcmp (criteria+1,"EYWORD"))
  730.       f = nntp_search_flag (nntp_search_keyword,&d);
  731.     break;
  732.       case 'N':            /* possible NEW */
  733.     if (!strcmp (criteria+1,"EW")) f = nntp_search_new;
  734.     break;
  735.  
  736.       case 'O':            /* possible OLD, ON */
  737.     if (!strcmp (criteria+1,"LD")) f = nntp_search_old;
  738.     else if (!strcmp (criteria+1,"N"))
  739.       f = nntp_search_date (nntp_search_on,&n);
  740.     break;
  741.       case 'R':            /* possible RECENT */
  742.     if (!strcmp (criteria+1,"ECENT")) f = nntp_search_recent;
  743.     break;
  744.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  745.     if (!strcmp (criteria+1,"EEN")) f = nntp_search_seen;
  746.     else if (!strcmp (criteria+1,"INCE"))
  747.       f = nntp_search_date (nntp_search_since,&n);
  748.     else if (!strcmp (criteria+1,"UBJECT"))
  749.       f = nntp_search_string (nntp_search_subject,&d,&n);
  750.     break;
  751.       case 'T':            /* possible TEXT, TO */
  752.     if (!strcmp (criteria+1,"EXT"))
  753.       f = nntp_search_string (nntp_search_text,&d,&n);
  754.     else if (!strcmp (criteria+1,"O"))
  755.       f = nntp_search_string (nntp_search_to,&d,&n);
  756.     break;
  757.       case 'U':            /* possible UN* */
  758.     if (criteria[1] == 'N') {
  759.       if (!strcmp (criteria+2,"ANSWERED")) f = nntp_search_unanswered;
  760.       else if (!strcmp (criteria+2,"DELETED")) f = nntp_search_undeleted;
  761.       else if (!strcmp (criteria+2,"FLAGGED")) f = nntp_search_unflagged;
  762.       else if (!strcmp (criteria+2,"KEYWORD"))
  763.         f = nntp_search_flag (nntp_search_unkeyword,&d);
  764.       else if (!strcmp (criteria+2,"SEEN")) f = nntp_search_unseen;
  765.     }
  766.     break;
  767.       default:            /* we will barf below */
  768.     break;
  769.       }
  770.       if (!f) {            /* if can't determine any criteria */
  771.     sprintf (LOCAL->buf,"Unknown search criterion: %.80s",criteria);
  772.     mm_log (LOCAL->buf,ERROR);
  773.     return;
  774.       }
  775.                 /* run the search criterion */
  776.       for (i = 1; i <= stream->nmsgs; ++i)
  777.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  778.       mail_elt (stream,i)->searched = NIL;
  779.     }
  780.                 /* report search results to main program */
  781.     for (i = 1; i <= stream->nmsgs; ++i)
  782.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  783.   }
  784. }
  785.  
  786. /* NNTP mail ping mailbox
  787.  * Accepts: MAIL stream
  788.  * Returns: T if stream alive, else NIL
  789.  */
  790.  
  791. long nntp_ping (MAILSTREAM *stream)
  792. {
  793.   /* Kludge alert: SMTPSOFTFATAL is 421 which is used in NNTP to mean ``No
  794.    * next article in this group''.  Hopefully, no NNTP server will send this
  795.    * in response to a STAT */
  796.   return (nntp_send (LOCAL->nntpstream,"STAT",NIL) != SMTPSOFTFATAL);
  797. }
  798.  
  799.  
  800. /* NNTP mail check mailbox
  801.  * Accepts: MAIL stream
  802.  */
  803.  
  804. void nntp_check (MAILSTREAM *stream)
  805. {
  806.                 /* never do if no updates */
  807.   if (LOCAL->dirty) newsrc_write (LOCAL->name,stream,LOCAL->number);
  808.   LOCAL->dirty = NIL;
  809. }
  810.  
  811. /* NNTP mail expunge mailbox
  812.  * Accepts: MAIL stream
  813.  */
  814.  
  815. void nntp_expunge (MAILSTREAM *stream)
  816. {
  817.   if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",NIL);
  818. }
  819.  
  820.  
  821. /* NNTP mail copy message(s)
  822.  * Accepts: MAIL stream
  823.  *        sequence
  824.  *        destination mailbox
  825.  * Returns: T if copy successful, else NIL
  826.  */
  827.  
  828. long nntp_copy (MAILSTREAM *stream,char *sequence,char *mailbox)
  829. {
  830.   mm_log ("Copy not valid for NNTP",ERROR);
  831.   return NIL;
  832. }
  833.  
  834.  
  835. /* NNTP mail move message(s)
  836.  * Accepts: MAIL stream
  837.  *        sequence
  838.  *        destination mailbox
  839.  * Returns: T if move successful, else NIL
  840.  */
  841.  
  842. long nntp_move (MAILSTREAM *stream,char *sequence,char *mailbox)
  843. {
  844.   mm_log ("Move not valid for NNTP",ERROR);
  845.   return NIL;
  846. }
  847.  
  848.  
  849. /* NNTP mail append message from stringstruct
  850.  * Accepts: MAIL stream
  851.  *        destination mailbox
  852.  *        stringstruct of messages to append
  853.  * Returns: T if append successful, else NIL
  854.  */
  855.  
  856. long nntp_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  857.           STRING *message)
  858. {
  859.   mm_log ("Append not valid for NNTP",ERROR);
  860.   return NIL;
  861. }
  862.  
  863. /* NNTP garbage collect stream
  864.  * Accepts: Mail stream
  865.  *        garbage collection flags
  866.  */
  867.  
  868. void nntp_gc (MAILSTREAM *stream,long gcflags)
  869. {
  870.   unsigned long i;
  871.   if (!stream->halfopen)     /* never on half-open stream */
  872.     if (gcflags & GC_TEXTS)    /* garbage collect texts? */
  873.                 /* flush texts from cache */
  874.       for (i = 0; i < stream->nmsgs; i++) {
  875.     if (LOCAL->header[i]) fs_give ((void **) &LOCAL->header[i]);
  876.     if (LOCAL->body[i]) fs_give ((void **) &LOCAL->body[i]);
  877.       }
  878. }
  879.  
  880. /* Internal routines */
  881.  
  882.  
  883. /* Parse flag list
  884.  * Accepts: MAIL stream
  885.  *        flag list as a character string
  886.  * Returns: flag command list
  887.  */
  888.  
  889. short nntp_getflags (MAILSTREAM *stream,char *flag)
  890. {
  891.   char *t,tmp[MAILTMPLEN],err[MAILTMPLEN];
  892.   short f = 0;
  893.   short i,j;
  894.   if (flag && *flag) {        /* no-op if no flag string */
  895.                 /* check if a list and make sure valid */
  896.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  897.       mm_log ("Bad flag list",ERROR);
  898.       return NIL;
  899.     }
  900.                 /* copy the flag string w/o list construct */
  901.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  902.     tmp[j] = '\0';
  903.     t = ucase (tmp);        /* uppercase only from now on */
  904.  
  905.     while (t && *t) {        /* parse the flags */
  906.       if (*t == '\\') {        /* system flag? */
  907.     switch (*++t) {        /* dispatch based on first character */
  908.     case 'S':        /* possible \Seen flag */
  909.       if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
  910.       t += 4;        /* skip past flag name */
  911.       break;
  912.     case 'D':        /* possible \Deleted flag */
  913.       if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  914.           t[5] == 'E' && t[6] == 'D') i = fDELETED;
  915.       t += 7;        /* skip past flag name */
  916.       break;
  917.     case 'F':        /* possible \Flagged flag */
  918.       if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  919.           t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
  920.       t += 7;        /* skip past flag name */
  921.       break;
  922.     case 'A':        /* possible \Answered flag */
  923.       if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  924.           t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
  925.       t += 8;        /* skip past flag name */
  926.       break;
  927.     default:        /* unknown */
  928.       i = 0;
  929.       break;
  930.     }
  931.                 /* add flag to flags list */
  932.     if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
  933.       }
  934.       else {            /* no user flags yet */
  935.     t = strtok (t," ");    /* isolate flag name */
  936.     sprintf (err,"Unknown flag: %.80s",t);
  937.     t = strtok (NIL," ");    /* get next flag */
  938.     mm_log (err,ERROR);
  939.       }
  940.     }
  941.   }
  942.   return f;
  943. }
  944.  
  945. /* Search support routines
  946.  * Accepts: MAIL stream
  947.  *        message number
  948.  *        pointer to additional data
  949.  *        pointer to temporary buffer
  950.  * Returns: T if search matches, else NIL
  951.  */
  952.  
  953. char nntp_search_all (MAILSTREAM *stream,long msgno,char *d,long n)
  954. {
  955.   return T;            /* ALL always succeeds */
  956. }
  957.  
  958.  
  959. char nntp_search_answered (MAILSTREAM *stream,long msgno,char *d,long n)
  960. {
  961.   return mail_elt (stream,msgno)->answered ? T : NIL;
  962. }
  963.  
  964.  
  965. char nntp_search_deleted (MAILSTREAM *stream,long msgno,char *d,long n)
  966. {
  967.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  968. }
  969.  
  970.  
  971. char nntp_search_flagged (MAILSTREAM *stream,long msgno,char *d,long n)
  972. {
  973.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  974. }
  975.  
  976.  
  977. char nntp_search_keyword (MAILSTREAM *stream,long msgno,char *d,long n)
  978. {
  979.   return NIL;            /* keywords not supported yet */
  980. }
  981.  
  982.  
  983. char nntp_search_new (MAILSTREAM *stream,long msgno,char *d,long n)
  984. {
  985.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  986.   return (elt->recent && !elt->seen) ? T : NIL;
  987. }
  988.  
  989. char nntp_search_old (MAILSTREAM *stream,long msgno,char *d,long n)
  990. {
  991.   return mail_elt (stream,msgno)->recent ? NIL : T;
  992. }
  993.  
  994.  
  995. char nntp_search_recent (MAILSTREAM *stream,long msgno,char *d,long n)
  996. {
  997.   return mail_elt (stream,msgno)->recent ? T : NIL;
  998. }
  999.  
  1000.  
  1001. char nntp_search_seen (MAILSTREAM *stream,long msgno,char *d,long n)
  1002. {
  1003.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1004. }
  1005.  
  1006.  
  1007. char nntp_search_unanswered (MAILSTREAM *stream,long msgno,char *d,long n)
  1008. {
  1009.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1010. }
  1011.  
  1012.  
  1013. char nntp_search_undeleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1014. {
  1015.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1016. }
  1017.  
  1018.  
  1019. char nntp_search_unflagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1020. {
  1021.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1022. }
  1023.  
  1024.  
  1025. char nntp_search_unkeyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1026. {
  1027.   return T;            /* keywords not supported yet */
  1028. }
  1029.  
  1030.  
  1031. char nntp_search_unseen (MAILSTREAM *stream,long msgno,char *d,long n)
  1032. {
  1033.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1034. }
  1035.  
  1036. char nntp_search_before (MAILSTREAM *stream,long msgno,char *d,long n)
  1037. {
  1038.   return (char) (nntp_msgdate (stream,msgno) < n);
  1039. }
  1040.  
  1041.  
  1042. char nntp_search_on (MAILSTREAM *stream,long msgno,char *d,long n)
  1043. {
  1044.   return (char) (nntp_msgdate (stream,msgno) == n);
  1045. }
  1046.  
  1047.  
  1048. char nntp_search_since (MAILSTREAM *stream,long msgno,char *d,long n)
  1049. {
  1050.                 /* everybody interprets "since" as .GE. */
  1051.   return (char) (nntp_msgdate (stream,msgno) >= n);
  1052. }
  1053.  
  1054.  
  1055. unsigned long nntp_msgdate (MAILSTREAM *stream,long msgno)
  1056. {
  1057.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1058.                 /* get date if don't have it yet */
  1059.   if (!elt->day) nntp_fetchstructure (stream,msgno,NIL);
  1060.   return (long) (elt->year << 9) + (elt->month << 5) + elt->day;
  1061. }
  1062.  
  1063.  
  1064. char nntp_search_body (MAILSTREAM *stream,long msgno,char *d,long n)
  1065. {
  1066.   char *t = nntp_fetchtext_work (stream,msgno);
  1067.   return (t && search (t,(unsigned long) strlen (t),d,n));
  1068. }
  1069.  
  1070.  
  1071. char nntp_search_subject (MAILSTREAM *stream,long msgno,char *d,long n)
  1072. {
  1073.   char *t = nntp_fetchstructure (stream,msgno,NIL)->subject;
  1074.   return t ? search (t,strlen (t),d,n) : NIL;
  1075. }
  1076.  
  1077.  
  1078. char nntp_search_text (MAILSTREAM *stream,long msgno,char *d,long n)
  1079. {
  1080.   char *t = nntp_fetchheader (stream,msgno);
  1081.   return (t && search (t,strlen (t),d,n)) ||
  1082.     nntp_search_body (stream,msgno,d,n);
  1083. }
  1084.  
  1085. char nntp_search_bcc (MAILSTREAM *stream,long msgno,char *d,long n)
  1086. {
  1087.   ADDRESS *a = nntp_fetchstructure (stream,msgno,NIL)->bcc;
  1088.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1089.                 /* get text for address */
  1090.   rfc822_write_address (LOCAL->buf,a);
  1091.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1092. }
  1093.  
  1094.  
  1095. char nntp_search_cc (MAILSTREAM *stream,long msgno,char *d,long n)
  1096. {
  1097.   ADDRESS *a = nntp_fetchstructure (stream,msgno,NIL)->cc;
  1098.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1099.                 /* get text for address */
  1100.   rfc822_write_address (LOCAL->buf,a);
  1101.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1102. }
  1103.  
  1104.  
  1105. char nntp_search_from (MAILSTREAM *stream,long msgno,char *d,long n)
  1106. {
  1107.   ADDRESS *a = nntp_fetchstructure (stream,msgno,NIL)->from;
  1108.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1109.                 /* get text for address */
  1110.   rfc822_write_address (LOCAL->buf,a);
  1111.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1112. }
  1113.  
  1114.  
  1115. char nntp_search_to (MAILSTREAM *stream,long msgno,char *d,long n)
  1116. {
  1117.   ADDRESS *a = nntp_fetchstructure (stream,msgno,NIL)->to;
  1118.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1119.                 /* get text for address */
  1120.   rfc822_write_address (LOCAL->buf,a);
  1121.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1122. }
  1123.  
  1124. /* Search parsers */
  1125.  
  1126.  
  1127. /* Parse a date
  1128.  * Accepts: function to return
  1129.  *        pointer to date integer to return
  1130.  * Returns: function to return
  1131.  */
  1132.  
  1133. search_t nntp_search_date (search_t f,long *n)
  1134. {
  1135.   long i;
  1136.   char *s;
  1137.   MESSAGECACHE elt;
  1138.                 /* parse the date and return fn if OK */
  1139.   return (nntp_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1140.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1141. }
  1142.  
  1143. /* Parse a flag
  1144.  * Accepts: function to return
  1145.  *        pointer to string to return
  1146.  * Returns: function to return
  1147.  */
  1148.  
  1149. search_t nntp_search_flag (search_t f,char **d)
  1150. {
  1151.                 /* get a keyword, return if OK */
  1152.   return (*d = strtok (NIL," ")) ? f : NIL;
  1153. }
  1154.  
  1155.  
  1156. /* Parse a string
  1157.  * Accepts: function to return
  1158.  *        pointer to string to return
  1159.  *        pointer to string length to return
  1160.  * Returns: function to return
  1161.  */
  1162.  
  1163. search_t nntp_search_string (search_t f,char **d,long *n)
  1164. {
  1165.   char *end = " ";
  1166.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1167.   if (!c) return NIL;        /* missing argument */
  1168.   switch (*c) {            /* see what the argument is */
  1169.   case '{':            /* literal string */
  1170.     *n = strtol (c+1,d,10);    /* get its length */
  1171.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  1172.     (!(*(c = *d + *n)) || (*c == ' '))) {
  1173.       char e = *--c;
  1174.       *c = DELIM;        /* make sure not a space */
  1175.       strtok (c," ");        /* reset the strtok mechanism */
  1176.       *c = e;            /* put character back */
  1177.       break;
  1178.     }
  1179.   case '\0':            /* catch bogons */
  1180.   case ' ':
  1181.     return NIL;
  1182.   case '"':            /* quoted string */
  1183.     if (strchr (c+1,'"')) end = "\"";
  1184.     else return NIL;
  1185.   default:            /* atomic string */
  1186.     if (*d = strtok (c,end)) *n = strlen (*d);
  1187.     else return NIL;
  1188.     break;
  1189.   }
  1190.   return f;
  1191. }
  1192.